#include"myheadd.h"

typedef struct 
{
    int sockfd;
    struct sockaddr_in group_addr;
    int *running;
    pthread_mutex_t *lock;  // 添加互斥锁
    char *username;

    //聊天记录
    char *multicast_ip;  // 新增：组播IP
    int port;            // 新增：组播端口
    //聊天记录

} ThreadArgs;

// 接收消息线程函数（修改后）
void *recv_thread(void *arg) 
{
    ThreadArgs *args = (ThreadArgs *)arg;
    char buffer[1024];
    struct sockaddr_in sender_addr;
    socklen_t addr_len = sizeof(sender_addr);
    int local_running = 1;
    
    // 设置套接字超时，避免永久阻塞
    struct timeval tv;
    tv.tv_sec = 3;
    tv.tv_usec = 0;
    setsockopt(args->sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    
    while (local_running) 
    {
        // 清空缓冲区
        memset(buffer, 0, sizeof(buffer));
        
        // 接收消息
        ssize_t len = recvfrom(args->sockfd, buffer, sizeof(buffer) - 1, 0,
                              (struct sockaddr *)&sender_addr, &addr_len);




         ////聊天记录
        char group_id[30];
        snprintf(group_id, 30, "%s:%d", args->multicast_ip, args->port); 
        // 从buffer解析出发送者（格式：[用户名] 消息）
        char sender[20], content[300];
        if (sscanf(buffer, "[%19s] %299[^\n]", sender, content) == 2) 
        {
            write_history(GROUP_CHAT, sender, group_id, content);
        }
        ////聊天记录
        
        if (len > 0) 
        {
            
            printf("%s\n", buffer);  // 直接显示带用户名的消息
        }
        else if (len < 0) 
        {
            // 超时或其他错误，检查是否需要退出
            if (errno == EAGAIN || errno == EWOULDBLOCK) 
            {
                // 超时，检查运行标志
                pthread_mutex_lock(args->lock);
                local_running = *(args->running);
                pthread_mutex_unlock(args->lock);
                continue;
            }
        }
        
        // 检查运行标志
        pthread_mutex_lock(args->lock);
        local_running = *(args->running);
        pthread_mutex_unlock(args->lock);
    }
    return NULL;
}

// 发送消息线程函数（修改后）
void *send_thread(void *arg) 
{
    ThreadArgs *args = (ThreadArgs *)arg;
    char buffer[1024];
    char send_buf[2048];//增加的名字
    printf("已加入组播，输入消息发送（exit退出）\n");
    while (1) 
    {
        // 清空缓冲区
        memset(buffer, 0, sizeof(buffer));
        memset(send_buf, 0, sizeof(send_buf));
        // 读取用户输入
        if (fgets(buffer, sizeof(buffer) - 1, stdin) != NULL) 
        {
            // 去除换行符
            buffer[strcspn(buffer, "\n")] = '\0';
            if (strcmp(buffer, "exit") == 0) 
            {
                printf("准备退出组播...\n");
                pthread_mutex_lock(args->lock);
                *(args->running) = 0;  // 设置退出标志
                pthread_mutex_unlock(args->lock);
                
                // 发送一个空包唤醒接收线程
                sendto(args->sockfd, "", 0, 0,(struct sockaddr *)&args->group_addr, sizeof(args->group_addr));
                break;
            }
            // 拼接用户名和消息内容（格式：[用户名] 消息）
           snprintf(send_buf, sizeof(send_buf), "[%s] %s", args->username, buffer);
            // 发送消息到组播地址
            sendto(args->sockfd, send_buf, strlen(send_buf), 0,(struct sockaddr *)&args->group_addr, sizeof(args->group_addr));
            
             ////聊天记录
            char group_id[30];
            snprintf(group_id, 30, "%s:%d", args->multicast_ip, args->port);  // 修改后  // 群标识格式：IP:端口
            write_history(GROUP_CHAT, args->username, group_id, buffer);
            ////聊天记录
        }
    }
    return NULL;
}

// 修改后的ccc函数
void ccc(char *uip, int uport ,char *username)
{
    char *multicast_ip = uip;
    int port = uport;
    int running = 1; //退出标志位  1运行  0退出
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥锁
    
    printf("组播ip:%s , 端口:%d\n", multicast_ip, port);
    
    // 创建UDP套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) 
    {
        perror("创建套接字失败");
        return;
    }

    // 设置端口复用，允许多个进程绑定同一端口
    int reuse = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) 
    {
        perror("设置端口复用失败");
        close(sockfd);
        return;
    }

    // 绑定到本地端口
    struct sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(port);

    if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
        perror("绑定端口失败");
        close(sockfd);
        return;
    }

    // 加入组播组
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr(multicast_ip);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) 
    {
        perror("加入组播组失败");
        close(sockfd);
        return;
    }

    // 设置组播目标地址
    struct sockaddr_in group_addr;
    memset(&group_addr, 0, sizeof(group_addr));
    group_addr.sin_family = AF_INET;
    group_addr.sin_addr.s_addr = inet_addr(multicast_ip);
    group_addr.sin_port = htons(port);

    // 准备线程参数
    ThreadArgs args;
    args.sockfd = sockfd;
    args.group_addr = group_addr;
    args.running = &running;
    args.lock = &lock;
    args.username = username; 

    //聊天记录
    args.multicast_ip = multicast_ip;  // 传入组播IP
    args.port = port;                  // 传入组播端口
    //聊天记录
    // 创建接收线程
    pthread_t recv_tid;
    if (pthread_create(&recv_tid, NULL, recv_thread, &args) != 0) 
    {
        perror("创建接收线程失败");
        close(sockfd);
        return;
    }

    // 创建发送线程
    pthread_t send_tid;
    if (pthread_create(&send_tid, NULL, send_thread, &args) != 0) 
    {
        perror("创建发送线程失败");
        pthread_cancel(recv_tid);
        close(sockfd);
        return;
    }

    // 等待线程结束
    pthread_join(send_tid, NULL);
    pthread_join(recv_tid, NULL);

    // 清理资源
    setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
    close(sockfd);
    pthread_mutex_destroy(&lock);
    printf("已退出群聊\n");
}